package de.ars.client.camera;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

import jzlib.JZlib;
import jzlib.ZStream;
 
public class imageProcessor {
    private static final byte[] SIGNATURE = new byte[] {
            (byte) 137, (byte) 80, (byte) 78, (byte) 71,
            (byte) 13, (byte) 10, (byte) 26, (byte) 10
            }; //png Signature

    public static byte[] toPNG(Image img, boolean processAlpha) throws IllegalArgumentException {
        img = resizeImage(img, 111, 89);
    	int width = img.getWidth();
        int height = img.getHeight();
        int[] argb = new int[width * height];
        img.getRGB(argb, 0, width, 0, 0, width, height);
        img = null;
        //got image dimensions... allowing garbage collection
        ByteArrayOutputStream png;
        try {
            byte[] header = HeaderChunk(width, height, processAlpha);
            byte[] data = DataChunk(width, height, argb, processAlpha);
            byte[] trailer = TrailerChunk();
 
            png = new ByteArrayOutputStream(SIGNATURE.length + header.length + data.length + trailer.length);
            png.write(SIGNATURE);
            png.write(header);
            png.write(data);
            png.write(trailer);
        } catch (IOException ioe) {
            throw new IllegalStateException("Unexpected " + ioe);
        }
        return png.toByteArray();
    }    
 
    private static byte[] HeaderChunk(int width, int height, boolean processAlpha) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(13);
        DataOutputStream chunk = new DataOutputStream(baos);
        chunk.writeInt(width);
        chunk.writeInt(height);
        chunk.writeByte(8);                        // Bitdepth
        chunk.writeByte(processAlpha ? 6 : 2);     // Colortype ARGB or RGB
        chunk.writeByte(0);                        // Compression
        chunk.writeByte(0);                        // Filter
        chunk.writeByte(0);                        // Interlace    
        return WriteToChunk("IHDR", baos.toByteArray());
    }
 
    private static byte[] DataChunk(int width, int height, int[] argb, boolean processAlpha) throws IOException, IllegalArgumentException {
        if (argb.length != (width * height)) {
            throw new IllegalArgumentException("array size does not match image dimensions");
        } 
        int source = 0;
        int dest = 0;
        byte[] raw = new byte[(processAlpha ? 4 : 3) * (width * height) + height];
        for (int y = 0; y < height; y++) {
            raw[dest++] = 0;                            // No filter
            for (int x = 0; x < width; x++) {
                int pixel = argb[source++];
                raw[dest++] = (byte)(pixel >> 16);      // red
                raw[dest++] = (byte)(pixel >> 8);       // green
                raw[dest++] = (byte)(pixel);            // blue
                if (processAlpha) {
                    raw[dest++] = (byte)(pixel >> 24);  // alpha
                }
            }
        }
        return WriteToChunk("IDAT", toJZLIB(raw));
    }
 
    private static byte[] TrailerChunk() throws IOException {
        return WriteToChunk("IEND", new byte[] {});
    }
 
    private static byte[] WriteToChunk(String id, byte[] raw) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(raw.length + 12);
        DataOutputStream chunk = new DataOutputStream(baos);
 
        chunk.writeInt(raw.length);
 
        byte[] bid = new byte[4];
        for (int i = 0; i < 4; i++) {
            bid[i] = (byte) id.charAt(i);
        }
 
        chunk.write(bid);
 
        chunk.write(raw);
 
        int crc = 0xFFFFFFFF;
        crc = updateCRC(crc, bid);
        crc = updateCRC(crc, raw);
        chunk.writeInt(~crc);
 
        return baos.toByteArray();
    }
 
    private static int[] crcTable = null;
 
    private static void createCRCTable() {
        crcTable = new int[256];
 
        for (int i = 0; i < 256; i++) {
            int c = i;
            for (int k = 0; k < 8; k++) {
                c = ( (c & 1) > 0) ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
            }
            crcTable[i] = c;
        }
    }
 
    private static int updateCRC(int crc, byte[] raw) {
        if (crcTable == null) {
            createCRCTable();
        }
 
        for (int i = 0; i < raw.length; i++) {
            crc = crcTable[ (crc ^ raw[i]) & 0xFF] ^ (crc >>> 8);
        }
 
        return crc;
    }
    
    public static byte[] toJZLIB(byte[] raw) {
	    byte[] compr = new byte[raw.length];
	    ZStream zs = new ZStream();
	    zs.deflateInit(JZlib.Z_BEST_COMPRESSION);
	    zs.next_in = raw;
	    zs.next_in_index = 0;
	    zs.avail_in = raw.length;
	    zs.next_out = compr;
	    zs.next_out_index = 0;
	    zs.avail_out = compr.length;
	    zs.deflate(JZlib.Z_NO_FLUSH);
	    zs.deflate(JZlib.Z_FINISH);
	    zs.deflateEnd();
	    byte[] outData = new byte[(int)zs.total_out];
	    System.arraycopy(compr, 0, outData, 0, outData.length);
	    return outData;
    }
     public static Image resizeImage(Image src, int width, int height) {
        int srcWidth = src.getWidth();
        int srcHeight = src.getHeight();
        if (width > srcWidth)
	   		 width = srcWidth;
	   	 if (height > srcHeight)
	   		 height = srcHeight;
        Image tmp = Image.createImage(width, srcHeight);
        Graphics g = tmp.getGraphics();
        int ratio = (srcWidth << 16) / width;
        int pos = ratio/2;

        //Horizontal Resize        

        for (int x = 0; x < width; x++) {
            g.setClip(x, 0, 1, srcHeight);
            g.drawImage(src, x - (pos >> 16), 0, Graphics.LEFT | Graphics.TOP);
            pos += ratio;
        }

        Image resizedImage = Image.createImage(width, height);
        g = resizedImage.getGraphics();
        ratio = (srcHeight << 16) / height;
        pos = ratio/2;        

        //Vertical resize

        for (int y = 0; y < height; y++) {
            g.setClip(0, y, width, 1);
            g.drawImage(tmp, 0, y - (pos >> 16), Graphics.LEFT | Graphics.TOP);
            pos += ratio;
        }
        return resizedImage;

    }
}